package com.radar.algorithm;

import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import com.radar.common.GisConstants;
import com.radar.common.ProductType;
import com.radar.exception.RadarCaclError;
import com.radar.io.CinradRadar;
import com.radar.utils.RefObj;
import lombok.Data;
import org.meteoinfo.global.PointD;

import java.io.Serializable;
import java.util.*;
import java.util.List;
import java.util.stream.Collectors;

public class Storms {

    public static List<Storms.StormDt> getStorms(double[][] Z, int step, RefObj<Integer> count, int Tz, CinradRadar cinradRadar) {
        int pz = 40 / step;

        //region 低阈值识别
        boolean[][] boolvar = new boolean[Z.length][Z[0].length];
        for (int i = 0; i < boolvar.length; i++) {
            for (int j = 0; j < boolvar[0].length; j++) {
                if (Z[i][j] >= Tz) {
                    boolvar[i][j] = true;
                }
            }
        }
        TITAN2 titan = new TITAN2();
        List<List<List<PointD>>> Storms0 = titan.GetStorms(boolvar, pz);
        List<List<PointD>> lps0 = new ArrayList<>();
        PointD[] MC0 = new PointD[Storms0.size()];
        double[] avez0 = new double[Storms0.size()];
        for (int i = 0; i < Storms0.size(); i++) {
            lps0.add(titan.GetBoundary(Storms0.get(i)));
            RefObj<Double> aveZ = new RefObj<Double>(0.0);
            MC0[i] = titan.MC(Storms0.get(i), Z, aveZ);
            avez0[i] = aveZ.getValue();
        }
        List<List<PointD>> newlps0 = new ArrayList<>();
        for (int i = 0; i < lps0.size(); i++) {
            MC0[i].X = MC0[i].X * step;
            MC0[i].Y = MC0[i].Y * step;
            List<PointD> tmp = new ArrayList<>();
            for (int j = 0; j < lps0.get(i).size(); j++) {
                tmp.add(new PointD(lps0.get(i).get(j).X * step, lps0.get(i).get(j).Y * step));
            }
            newlps0.add(tmp);
        }
        //endregion 低阈值识别

        //region 腐蚀
        List<PointD> AllPoints = titan.GetAllPoints(Storms0);
        boolean[][] bv = new boolean[Z.length][Z[0].length];
        for (int i = 0; i < AllPoints.size(); i++) {
            bv[(int) AllPoints.get(i).X][(int) AllPoints.get(i).Y] = true;
        }
        boolean[][] bv2 = titan.ErodeBoundary(bv, Z, 20, Tz, Storms0);
        //endregion 腐蚀

        //region 再次识别
        //调低面积阈值
        List<List<List<PointD>>> Storms1 = titan.GetStorms(bv2, pz / 2);
        List<List<PointD>> lps1 = new ArrayList<>();
        PointD[] MC1 = new PointD[Storms1.size()];
        double[] avez1 = new double[Storms1.size()];
        for (int i = 0; i < Storms1.size(); i++) {
            lps1.add(titan.GetBoundary(Storms1.get(i)));
            RefObj<Double> aveZ = new RefObj<>(0.0);
            MC1[i] = titan.MC(Storms1.get(i), Z, aveZ);
            avez1[i] = aveZ.getValue();
        }
        List<List<PointD>> newlps1 = new ArrayList<>();
        for (int i = 0; i < lps1.size(); i++) {
            MC1[i].X = MC1[i].X * step;
            MC1[i].Y = MC1[i].Y * step;
            List<PointD> tmp = new ArrayList<>();
            for (int j = 0; j < lps1.get(i).size(); j++) {
                tmp.add(new PointD(lps1.get(i).get(j).X * step, lps1.get(i).get(j).Y * step));
            }
            newlps1.add(tmp);
        }
        //endregion 再次识别

        //region 判断哪个风暴需要膨胀操作
        int[] Index = new int[Storms1.size()];
        for (int i = 0; i < Storms1.size(); i++) {
            PointD p = Storms1.get(i).get(0).get(0);
            boolean find = false;
            int index = -1;
            for (int m = 0; m < Storms0.size(); m++) {
                for (int n = 0; n < Storms0.get(m).size(); n++) {
                    if (contains(Storms0.get(m).get(n), p)) {
                        find = true;
                        break;
                    }
                }
                if (find) {
                    index = m;
                    break;
                }
            }
            Index[i] = index;
        }

        boolean[] NeedDilate = new boolean[Storms1.size()];
        //DilateIndex[i][0]为低阈值风暴号，其后为经腐蚀后在其区域内新生成的风暴号。
        List<List<Integer>> DilateIndex = new ArrayList<>();
        for (int i = 0; i < Storms1.size() - 1; i++) {
            if (!NeedDilate[i]) {
                List<Integer> tmp = new ArrayList<>();
                for (int j = i + 1; j < Storms1.size(); j++) {
                    if (Index[i] == Index[j]) {
                        NeedDilate[i] = true;
                        NeedDilate[j] = true;
                        if (tmp.size() == 0) {
                            tmp.add(Index[i]);
                            tmp.add(i);
                        }
                        tmp.add(j);
                    }
                }
                if (tmp.size() != 0) {
                    DilateIndex.add(tmp);
                }
            }
        }
        //endregion 判断哪个风暴需要膨胀操作

        //不需要膨胀的点全部复原
        for (int i = 0; i < Storms1.size(); i++) {
            if (!NeedDilate[i]) {
                for (int j = 0; j < Storms0.get(Index[i]).size(); j++) {
                    for (int m = 0; m < Storms0.get(Index[i]).get(j).size(); m++) {
                        bv2[(int) Storms0.get(Index[i]).get(j).get(m).X][(int) Storms0.get(Index[i]).get(j).get(m).Y] = true;
                    }
                }
            }
        }

        //region 膨胀
        for (int i = 0; i < DilateIndex.size(); i++) {
            List<List<PointD>> lp = new ArrayList<>();
            lp.add(lps0.get(DilateIndex.get(i).get(0)));//大边界

            for (int j = 1; j < DilateIndex.get(i).size(); j++) {
                lp.add(lps1.get(DilateIndex.get(i).get(j)));
            }


            //region 根据风暴之间的最短距离判断膨胀的次数
            List<Integer>[] dis = new ArrayList[lp.size() - 1];
            for (int j = 1; j < lp.size(); j++) {
                dis[j - 1] = new ArrayList<>();
                for (int m = 0; m < lp.size(); m++) {
                    if (j != m) {
                        if (m == 0)
                            dis[j - 1].add((int) titan.BoundaryDistance(lp.get(j), lp.get(m)));
                        else
                            dis[j - 1].add((int) (titan.BoundaryDistance(lp.get(j), lp.get(m)) / 2 - 1));
                    }
                }
            }

            for (int j = 1; j < lp.size(); j++) {
                int mind = dis[j - 1].stream().min(Integer::compare).get();
                if (mind > 0) {
                    for (int m = 0; m < dis[j - 1].stream().min(Integer::compare).get(); m++) {
                        titan.DilateBoundary(lp.get(j), bv2);
                    }
                }
            }

            //endregion 根据风暴之间的最短距离判断膨胀的次数
        }
        //endregion 膨胀

        List<List<List<PointD>>> Storms;
        List<List<PointD>> lps;
        PointD[] MC;

        //膨胀后再识别
        Storms = titan.GetStorms(bv2, pz / 2);
        count.setValue(Storms.size());
        //边界与质心
        lps = new ArrayList<>();
        MC = new PointD[Storms.size()];
        double[] avez = new double[Storms.size()];
        for (int i = 0; i < Storms.size(); i++) {
            lps.add(titan.GetBoundary(Storms.get(i)));
            RefObj<Double> aveZ = new RefObj<>(0.0);
            MC[i] = titan.MC(Storms.get(i), Z, aveZ);
            avez[i] = aveZ.getValue();
        }

        List<List<PointD>> newlps = new ArrayList<>();
        for (int i = 0; i < lps.size(); i++) {
            MC[i].X = MC[i].X * step;
            MC[i].Y = MC[i].Y * step;
            List<PointD> tmp = new ArrayList<>();
            for (int j = 0; j < lps.get(i).size(); j++) {
                tmp.add(new PointD(lps.get(i).get(j).X * step, lps.get(i).get(j).Y * step));
            }
            newlps.add(tmp);
        }
        // 识别产品

        double[] a = new double[MC.length];
        double[] b = new double[MC.length];
        double[] tht = new double[MC.length];
        for (int ix = 0; ix < MC.length; ix++) {
            int c = 8;
            double[] x = new double[c];
            double[] y = new double[c];
            int s = newlps.get(ix).size() / c;
            for (int i = 0; i < c; i++) {
                x[i] = newlps.get(ix).get(s * i).X;
                y[i] = newlps.get(ix).get(s * i).Y;
            }
            List<Double> disx = new ArrayList<>();
            for (int i = 0; i < c; i++) {
                disx.add(Math.sqrt((x[i] - MC[ix].X) * (x[i] - MC[ix].X) + (y[i] - MC[ix].Y) * (y[i] - MC[ix].Y)));
            }
            double maxb = disx.stream().max(Double::compare).get();
            double minb = disx.stream().min(Double::compare).get();
            RefObj<Double> aref = new RefObj<>(0.0);
            RefObj<Double> bref = new RefObj<>(0.0);
            RefObj<Double> thtref = new RefObj<>(0.0);
            Ellipse(x, y, MC[ix].X, MC[ix].Y, maxb, minb, aref, bref, thtref);
            a[ix] = aref.getValue();
            b[ix] = bref.getValue();
            tht[ix] = thtref.getValue();
        }
        List<Storms.StormDt> list = new ArrayList<>();
        for (int i = 0; i < MC.length; i++) {
            Storms.StormDt stormDt = new Storms.StormDt();
            stormDt.编号(UUID.randomUUID().toString());
            stormDt.setObsDate(cinradRadar.radar.dateTime);
            stormDt.setId(i);
            int allpoint = 0;
            for (int j = 0; j < Storms.get(i).size(); j++) {
                allpoint += Storms.get(i).get(j).size();
            }
            //所有点的数目（面积）
            double A = allpoint * step * cinradRadar.radar.getResolution(ProductType.REF)
                    * step * cinradRadar.radar.getResolution(ProductType.REF) / 1000000.0;
            stormDt.setArea(A);
            //边界点
            List<PointD> bps = new ArrayList<>();
//            PointD[] bps = new PointD[newlps.get(i).size()];
            for (int j = 0; j < newlps.get(i).size(); j++) {
                bps.add(newlps.get(i).get(j));
            }
            stormDt.setBoundary(bps);
            //所有点的坐标
//            List<PointD> ps = new ArrayList<>();
            PointD[] ps = new PointD[allpoint];//所有点的坐标
            int c1 = 0;
            for (int j = 0; j < Storms.get(i).size(); j++) {
                c1 += Storms.get(i).get(j).size();
                for (int m = 0; m < Storms.get(i).get(j).size(); m++) {
                    ps[c1 - Storms.get(i).get(j).size() + m] = new PointD(Storms.get(i).get(j).get(m).X * step, Storms.get(i).get(j).get(m).Y * step);
                }
            }
            stormDt.setContain(Arrays.asList(ps));
            stormDt.setCenter(MC[i]);
            stormDt.setIntensity(avez[i]);
            stormDt.setEllipseParam(a[i] + "," + b[i] + "," + tht[i]);
            list.add(stormDt);
        }
        return list;
    }

    public static void Ellipse(double[] x, double[] y, double x0, double y0, double maxd, double mind, RefObj<Double> aref, RefObj<Double> bref, RefObj<Double> thtref) {
        List<Double> le = new ArrayList<>();
        int a0 = (int) (maxd - 10 > mind ? maxd - 10 : mind);
        for (int n = a0; n < maxd + 10; n++) {
            for (int j = (int) mind; j <= (int) maxd; j++) {
                for (int i = -90; i < 90; i++) {
                    double e = 0;
                    for (int m = 0; m < x.length; m++) {
                        double a1 = (x[m] - x0) * Math.cos(i * Math.PI / 180) + (y[m] - y0) * Math.sin(i * Math.PI / 180);
                        double b1 = (y[m] - y0) * Math.cos(i * Math.PI / 180) + (x[m] - x0) * Math.sin(i * Math.PI / 180);
                        e += Math.abs(a1 * a1 / (n * n) + b1 * b1 / (j * j) - 1);
                    }
                    le.add(e);
                }
            }
        }
        double mine = le.stream().min(Double::compare).get();
        int index = le.indexOf(mine);
        int c = (int) maxd - (int) mind + 1;
        aref.setValue((double) (index / (c * 180) + a0));
        int i2 = index % (c * 180);
        bref.setValue((double) (i2 / 180 + (int) mind));
        thtref.setValue((double) (i2 % 180 - 90));
    }

    public static boolean contains(String[] it, String item) {
        for (int i = 0; i < it.length; i++) {
            if (it[i].equals(item)) {
                return true;
            }
        }
        return false;
    }

    public static boolean contains(List<PointD> pointDS, PointD point) {
        for (int i = 0; i < pointDS.size(); i++) {
            if (point.X == pointDS.get(i).X && point.Y == pointDS.get(i).Y) {
                return true;
            }
        }
        return false;
    }

    public static int indexOf(List<PointD> pointDS, PointD point) {
        for (int i = 0; i < pointDS.size(); i++) {
            if (point.X == pointDS.get(i).X && point.Y == pointDS.get(i).Y) {
                return i;
            }
        }
        return -1;
    }

    public static double DisPP(PointD p1, PointD p2, double binlength) {
        return Math.sqrt((p1.X - p2.X) * (p1.X - p2.X) + (p1.Y - p2.Y) * (p1.Y - p2.Y)) * binlength / 1000.0;
    }

    public static List<Storms.StormDt> track(List<Storms.StormDt> preData, List<Storms.StormDt> curData, CinradRadar cinradRadar) throws RadarCaclError {
        //面积重叠比
        double _L = 0.15;
        //分裂或不变
        double _A1 = 0.3;
        //生长或合并
        double _A2 = -0.05;
        //产生分裂的最小面积
        double _A3 = 50;
        double _D0 = 5;
        double _D1 = 10;
        if (preData.size() == 0 || curData.size() == 0) {
            return null;
        }
        preData = preData.stream().sorted(Comparator.comparing(Storms.StormDt::getArea).reversed()).collect(Collectors.toList());
        curData = curData.stream().sorted(Comparator.comparing(Storms.StormDt::getArea).reversed()).collect(Collectors.toList());
        long interval = DateUtil.between(curData.get(0).obsDate, preData.get(0).obsDate, DateUnit.SECOND);
        if (interval > 1200 || interval <= 0) {
            return null;
        }
        for (int i = 0; i < curData.size(); i++) {
            List<PointD> p1 = curData.get(i).getContain();
            int n1 = p1.size();
            double curA = curData.get(i).getArea();
            PointD curMC = curData.get(i).getCenter();
            boolean match = false;
            //面积重叠
            for (int j = 0; j < preData.size(); j++) {
                if (preData.get(j).getContain() == null || preData.get(j).getContain().size() == 0)
                    continue;
                //与当前雷暴范围重合点数
                int ON = 0;//重合数
                double OR;//重合比例
                List<PointD> p0 = preData.get(j).getContain();
                int n0 = p0.size();
                if (n1 > n0) {
                    for (int m = 0; m < n0; m++) {
                        if (contains(p1, p0.get(m)))
                            ON++;
                    }
                    OR = ON * 1.0 / n0;
                } else {
                    for (int m = 0; m < n1; m++) {
                        if (contains(p0, p1.get(m)))
                            ON++;
                    }
                    OR = ON * 1.0 / n1;
                }

                if (OR > _L) {
                    //匹配
                    if (curData.get(i).对应上一时次编号 == null) {
                        curData.get(i).对应上一时次编号(preData.get(j).编号);
                    } else {
                        String tmp = curData.get(i).对应上一时次编号;
                        String[] it = tmp.split(",");
                        if (!contains(it, preData.get(j).编号)) {
                            curData.get(i).对应上一时次编号(curData.get(i).对应上一时次编号 + "," + preData.get(j).编号);
                        }
                    }
                    //面积变化率
                    double preA = preData.get(j).getArea();
                    double AR = (curA - preA) / preA;
                    if (AR > _A2) {
                        preData.get(j).setContain(null);
                    }
                    PointD preMC = preData.get(j).getCenter();
                    double MCDis = DisPP(curMC, preMC, cinradRadar.radar.getResolution(ProductType.REF));
                    if (AR < _A1) {
                        match = true;
                        break;//不在对其他云团进行匹配
                    }
                    if (MCDis < _D0) {
                        match = true;
                        break;//不在对其他云团进行匹配
                    }
                }
            }

            //质心距离1
            if (!match) {
                for (int j = 0; j < preData.size(); j++) {
                    if (preData.get(j).getContain() == null)
                        continue;
                    PointD preMC = preData.get(j).getCenter();
                    double MCDis = DisPP(curMC, preMC, cinradRadar.radar.getResolution(ProductType.REF));
                    if (MCDis < _D1) {
                        match = true;
                        //匹配
                        if (curData.get(i).对应上一时次编号 == null) {
                            curData.get(i).对应上一时次编号(preData.get(j).编号);
                        } else {
                            String tmp = curData.get(i).对应上一时次编号;
                            String[] it = tmp.split(",");
                            if (!contains(it, preData.get(j).编号)) {
                                curData.get(i).对应上一时次编号(curData.get(i).对应上一时次编号 + "," + preData.get(j).编号);
                            }
                        }
                        //面积变化率
                        double preA = preData.get(j).getArea();
                        double AR = (curA - preA) / preA;
                        if (AR > _A2 || preA < _A3) {
                            preData.get(j).setContain(null);
                        }
                        if (MCDis < _D0) {
                            break;//不在对其他云团进行匹配
                        }
                        if (AR < _A1) {
                            break;//不在对其他云团进行匹配
                        }
                    }
                }
            }
            //移速、移向计算
            if (curData.get(i).对应上一时次编号 != null) {
                String tmp = curData.get(i).对应上一时次编号;
                String[] ptmp = tmp.split(",");
                int[] index = new int[ptmp.length];
                double totleA = 0;
                for (int j = 0; j < ptmp.length; j++) {
                    for (int m = 0; m < preData.size(); m++) {
                        if (preData.get(m).编号.equals(ptmp[j])) {
                            index[j] = m;
                            break;
                        }
                    }
                }
                for (int j = 0; j < ptmp.length; j++) {
                    totleA += preData.get(index[j]).getArea();
                }
                double x = 0, y = 0;
                for (int j = 0; j < ptmp.length; j++) {
                    PointD p = preData.get(index[j]).getCenter();
                    double A = preData.get(index[j]).getArea();
                    x += (curMC.X - p.X) * A / totleA;//面积权重
                    y += (curMC.Y - p.Y) * A / totleA;
                }
                double spd = Math.sqrt(x * x + y * y) * cinradRadar.radar.getResolution(ProductType.REF) / interval;
                double dir = 270 - Math.atan2(y, x) * 180 / Math.PI;
                if (dir >= 360)
                    dir -= 360;
                curData.get(i).setWs(spd);
                curData.get(i).setWd(dir);
            }
        }

        //处理分裂时的移向问题
        for (int j = 0; j < preData.size(); j++) {
            String no = preData.get(j).编号;
            List<Integer> list = new ArrayList<>();
            for (int i = 0; i < curData.size(); i++) {
                if (curData.get(i).对应上一时次编号 != null) {
                    String tmp = curData.get(i).对应上一时次编号;
                    if (!tmp.contains(",")) {
                        if (tmp.equals(no)) {
                            list.add(i);
                        }
                    }
                }
            }
            if (list.size() > 1) {
                PointD p0 = preData.get(j).getCenter();
                double totleA = 0;
                for (int i = 0; i < list.size(); i++) {
                    totleA += curData.get(list.get(i)).getArea();
                }
                double x = 0, y = 0;
                for (int i = 0; i < list.size(); i++) {
                    PointD p = curData.get(list.get(i)).getCenter();
                    double A = curData.get(list.get(i)).getArea();
                    //面积权重
                    x += (p0.X - p.X) * A / totleA;
                    y += (p0.Y - p.Y) * A / totleA;
                }
                double spd = Math.sqrt(x * x + y * y) * cinradRadar.radar.getResolution(ProductType.REF) / interval;
                double dir = 270 - Math.atan2(y, x) * 180 / Math.PI;
                if (dir >= 360)
                    dir -= 360;
                dir = dir + 180;
                if (dir >= 360)
                    dir -= 360;
                for (int i = 0; i < list.size(); i++) {
                    curData.get(list.get(i)).setWs(spd);
                    curData.get(list.get(i)).setWd(dir);
                }
            }
        }
        //根据大的云团的移向来审查小云团移动方向是否正确
        if (curData.size() > 5) {
            double au = 0, av = 0;
            double AveSPD, AveDIR;
            for (int i = 0; i < 5; i++) {
                if (curData.get(i).ws != GisConstants.UNDEF) {
                    double spd = curData.get(i).ws;
                    double dir = curData.get(i).wd;
                    double u = spd * Math.sin(dir * Math.PI / 180);
                    double v = spd * Math.cos(dir * Math.PI / 180);
                    au += u;
                    av += v;
                }
            }
            AveSPD = Math.sqrt(au * au + av * av) / 5;
            AveDIR = 90 - Math.atan2(av, au) * 180 / Math.PI;
            if (AveDIR < 0) {
                AveDIR += 360;
            }
            for (int i = 6; i < curData.size(); i++) {
                if (curData.get(i).对应上一时次编号 != null) {
                    double dir = curData.get(i).wd;
                    double dirD = Math.abs(dir - AveDIR);
                    if (dirD > 180) {
                        dirD = 360 - dirD;
                    }
                    double spd = curData.get(i).ws;
                    double spdD = (spd - AveSPD) / AveSPD;
                    if (dirD > 90 || spdD > 0.5) {
                        curData.get(i).对应上一时次编号(null);
                        curData.get(i).ws = GisConstants.UNDEF;
                        curData.get(i).wd = GisConstants.UNDEF;
                    }
                }
            }
        }
        //到达本场时间计算(边界点到过本场、斜率为风向的直线的最短距离)
        int bins = cinradRadar.radar.getRaw(0, ProductType.REF)[0].length;
        //中心点
        PointD P0 = new PointD(bins, bins);
        for (int i = 0; i < curData.size(); i++) {
            if (curData.get(i).wd != GisConstants.UNDEF) {
                List<PointD> pt = curData.get(i).getContain();
                //计算移入时间
                if (!contains(pt, P0)) {
                    double a = 90 - curData.get(i).wd;
                    if (a < 0)
                        a += 360;
                    double k = Math.tan(a * Math.PI / 180);//斜率

                    double A = -1 * k;
                    double B = 1;
                    double C = k * P0.X - P0.Y;
                    List<PointD> ps = curData.get(i).getBoundary();
                    List<Double> dis = new ArrayList<>();
                    List<PointD> P = new ArrayList<>();
                    for (int j = 0; j < ps.size(); j++) {
                        double d = Math.abs(A * ps.get(j).X + B * ps.get(j).Y + C) / Math.sqrt(A * A + B * B);
                        dis.add(d);
                        if (d == 0) {
                            P.add(ps.get(j));
                        }
                    }
                    Double MinD = 0.0;
                    PointD MinP;
                    if (P.size() == 0) {
                    } else {
                        //可直接影响
                        List<Double> dis2 = new ArrayList<>();
                        for (int m = 0; m < P.size(); m++) {
                            dis2.add(DisPP(P.get(m), P0, cinradRadar.radar.getResolution(ProductType.REF)));
                        }
                        MinD = dis2.stream().min(Double::compare).get();
                        MinP = P.get(dis2.indexOf(MinD));
                        double a2 = Math.atan2(P0.Y - MinP.Y, P0.X - MinP.X) * 180 / Math.PI;
                        a2 = 90 - a2;
                        if (a2 < 0)
                            a2 += 360;
                        if (Math.abs(Math.abs(a2 - curData.get(i).wd) - 180) < 0.01) {
                            double v = curData.get(i).ws;
                            double t = MinD * 1000 / v / 60;
                            if (t < 120) {
                                curData.get(i).arriveTime = t;
                            }
                        }
                    }
                } else {
                    //计算移出时间

                }
            }
        }
        return curData;
    }

    @Data
    public static class StormDt implements Serializable {
        private String 编号;

        public void 编号(String var) {
            编号 = var;
        }

        private String 对应上一时次编号;

        public void 对应上一时次编号(String var) {
            对应上一时次编号 = var;
        }

        private int obsNum;
        private Date obsDate;
        private int id;
        private double area;
        private List<PointD> boundary;
        private List<PointD> contain;
        private PointD center;
        private double intensity;
        private String ellipseParam;
        private double ws = GisConstants.UNDEF;
        private double wd = GisConstants.UNDEF;
        private double arriveTime = GisConstants.UNDEF;
    }

}
